package com.dwy.decodemjpeg

import android.content.Context
import android.media.MediaCodec
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.Socket
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Queue
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.regex.Matcher

class MjpegMediaCodecPlayer(private val socket: Socket/*, private val surface: Surface*/) {
    private var mediaCodec: MediaCodec? = null
    private var running = false
    private val matcher: Matcher? = null
    /*private val pattern: Pattern*/
    private var onGetPicDataListener: OnGetPicDataListener? = null
    private var picDataHex: String? = null
    private var picDataHexTemp = ""
    private var picDataHexTempStartIndex = 0
    private val picStartData = "ffd8"
    private val picEndData = "ffd9"
    private val receiveQueue: Queue<ByteArray> = ConcurrentLinkedQueue()
    private var receiveQueueRunning = false
    private val picFragmentPackageQueue = ConcurrentLinkedQueue<PicFragmentPackage>()
    private var picFragmentPackageQueueRunning = false
    //private var picRunning = false
    var sourceDataPath = ""
    private var sourceDataRestoreOutputStream: FileOutputStream? = null

    var videoFilePath = ""
    private var videoDataRestoreOutputStream: FileOutputStream? = null

    var logPath = ""
    //private var prePackageDone = true
    private var picIndex = 0//图片帧序号，解析了多少张图片
    private var picData = byteArrayOf()//图片帧数据，完整的一张图片
    private var transtionData = byteArrayOf()//图片帧分片数据，其中一片数据
    private var transtionTempData = byteArrayOf()//从协议包头开始，不到一个协议包的数据
    private var singlePackageFinish = true
    private var currentPackage = byteArrayOf()

    private var singlePackageData = byteArrayOf()
    private var overLeftData = byteArrayOf()
    private var cPicFragmentPackage = PicFragmentPackage()

    /*init {
        pattern = Pattern.compile("d5f00100[a-z0-9]{32}")
        try {
            createMediaCodec()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }*/

    fun startPlayback(onGetPicDataListener: OnGetPicDataListener?) {
        this.onGetPicDataListener = onGetPicDataListener
        running = true
        picDataHex = ""
        picDataHexTemp = ""
        picDataHexTempStartIndex = 0
        receiveQueue.clear()
        picFragmentPackageQueue.clear()
        receiveQueueRunning = true
        //picRunning = true
        picFragmentPackageQueueRunning = true
        //prePackageDone = true
        picIndex = 0
        singlePackageFinish = true
        if (TestConfig.enableSourceDataRestore) {
            val file = File(sourceDataPath)
            try {
                if (!file.parentFile.exists()) {
                    file.parentFile.mkdirs()
                    if (!file.exists()) {
                        file.createNewFile()
                    }
                }
                sourceDataRestoreOutputStream = FileOutputStream(file)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        if (TestConfig.enableVideoDataRestore) {
            val file = File(videoFilePath)
            try {
                if (!file.parentFile.exists()) {
                    file.parentFile.mkdirs()
                    if (!file.exists()) {
                        file.createNewFile()
                    }
                }
                videoDataRestoreOutputStream = FileOutputStream(file)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        if (TestConfig.enableFileLog) {
            val logFile = File(logPath)
            try {
                if (!logFile.parentFile.exists()) {
                    logFile.parentFile.mkdirs()
                    if (!logFile.exists()) {
                        logFile.createNewFile()
                    }
                }
                logFileOutputStream = FileOutputStream(logFile, true)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        Thread {
            /*while (running) {
                try {
                    decodeMjpegFrame()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }*/
            try {
                /*ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
                    int inputBufferIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_US);
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                        inputBuffer.clear();
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, inputBuffer.position(), 0, 0);
                    }*/
                val buffer = ByteArray(1600)
                var bytesRead = 0
                while (true) {
                    while (running && !socket.isClosed && socket.getInputStream().read(buffer).also { bytesRead = it } != -1) {
                        //Log.d("1111", "size:$bytesRead,this:$this")
                        val realBuffer = ByteArray(bytesRead)
                        System.arraycopy(buffer, 0, realBuffer, 0, bytesRead)
                        //handleLog(if (TestConfig.enableBytes2HexLog) "bytesRead: ${ObjectUtil.bytes2Hex(realBuffer)}" else "bytesReadSize: ${realBuffer.size}")
                        handleLog("bytesRead: ${ObjectUtil.bytes2Hex(realBuffer)}")
                        handleLog("length:${realBuffer.size}")
                        receiveQueue.add(realBuffer)
                        handleLog("decodeMjpegFrame,当前队列大小:" + receiveQueue.size)
                    }
                }

                /*MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
                while (outputBufferIndex >= 0) {
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
                }*/
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }.start()
        Thread {
            while (true) {
                /*if (prePackageDone) {

                }*/
                if (!receiveQueueRunning) return@Thread
                if (receiveQueue.size > 0) {
                    handleLog("decodeRunning,当前队列大小:" + receiveQueue.size)
                    val packageData = receiveQueue.poll() ?: byteArrayOf()
                    handleLog("decodeRunning," + if (TestConfig.enableBytes2HexLog) "packageData:" + ObjectUtil.bytes2Hex(packageData) else "packageDataSize:" + packageData.size)
                    if (packageData.isNotEmpty()) {
                        if (TestConfig.enableSourceDataRestore) {
                            Thread {
                                try {
                                    sourceDataRestoreOutputStream!!.write(packageData)
                                } catch (e: IOException) {
                                    throw RuntimeException(e)
                                }
                            }.start()
                        }
                        parsePic2(packageData)
                    }
                }
            }
        }.start()

        Thread {
            var picPackage = PicPackage()
            while (true) {
                /*if (picFragmentPackageQueue.size > 0) {
                    handleLog("dataPackageRunning,当前队列大小:" + picFragmentPackageQueue.size)
                }*/
                if (!picFragmentPackageQueueRunning) return@Thread
                val picFragmentPackage = picFragmentPackageQueue.poll()
                if (picFragmentPackage != null) {
                    handleLog("小帧组大帧，当前小帧:$picFragmentPackage")
                    val payload = byteArrayOf(picFragmentPackage.id.toByte(), picFragmentPackage.eof.toByte(), picFragmentPackage.count.toByte(), picFragmentPackage.size.toByte()).plus(picFragmentPackage.data)
                    val crc = caculateCrc(payload)
                    handleLog("小帧组大帧，当前小帧crc:${ObjectUtil.bytes2Hex(byteArrayOf(crc))}")
                    if (!TestConfig.checkCrc || caculateCrc(payload) == picFragmentPackage.crc) {
                        if (picFragmentPackage.eof.toInt() == 0 && picFragmentPackage.count < picFragmentPackage.size) {
                            if (picFragmentPackage.count.toInt() == 1) {
                                picPackage = PicPackage().apply { data = picFragmentPackage.data }
                            } else {
                                picPackage.apply { data = data.plus(picFragmentPackage.data) }
                            }
                        } else if (picFragmentPackage.eof.toInt() == 1 && picFragmentPackage.count == picFragmentPackage.size) {
                            if (picFragmentPackage.count.toInt() == 1 && picPackage.data.isEmpty()) {
                                picPackage = PicPackage().apply {
                                    id = picFragmentPackage.id
                                    data = picFragmentPackage.data
                                }
                            } else {
                                picPackage.apply {
                                    id = picFragmentPackage.id
                                    data = data.plus(picFragmentPackage.data)
                                }
                            }
                            handleLog("小帧组大帧，组完一个大帧:$picPackage")
                            if (TestConfig.enableVideoDataRestore && videoDataRestoreOutputStream != null) {
                                Thread {
                                    videoDataRestoreOutputStream?.write(picPackage.data)
                                }.start()
                            }
                            onGetPicDataListener?.onGetPicData(picPackage)
                        }
                    } else {
                        handleLog("小帧组大帧，校验失败:$picFragmentPackage")
                    }
                }
            }
        }.start()
    }

    interface OnGetPicDataListener {
        fun onGetPicData(picPackage: PicPackage)
    }

    data class PicPackage(var id: UByte = 0u,
                          var data: ByteArray = byteArrayOf(),) {
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false

            other as PicPackage

            if (id != other.id) return false
            if (!data.contentEquals(other.data)) return false

            return true
        }

        override fun hashCode(): Int {
            var result = id.toInt()
            result = 31 * result + data.contentHashCode()
            return result
        }

        override fun toString(): String {
            return "PicPackage[id:$id," + if (TestConfig.enableBytes2HexLog) "data:" + ObjectUtil.bytes2Hex(data) else "dataSize:" + data.size.toString() + "]"
        }
    }

    data class PicFragmentPackage(val lb: Byte = -1,
                                  val hb: Byte = -1,
                                  val flags: ByteArray = byteArrayOf(),
                                  val timestamp: ByteArray = byteArrayOf(),
                                  val sequence: ByteArray = byteArrayOf(),
                                  val length: ByteArray = byteArrayOf(),
                                  val lengthInt: UInt = 0u,
                                  val realDataLength: UInt = 0u,
                                  val crc: Byte = -1,
                                  val reversed: ByteArray = byteArrayOf(),
                                  val id: UByte = 0u,
                                  val eof: UByte = 0u,
                                  val count: UByte = 0u,
                                  val size: UByte = 0u,
                                  var data: ByteArray = byteArrayOf(),
                                  var finish: Boolean = false,
                                  var overLeftData: ByteArray = byteArrayOf(), ) {
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false

            other as PicFragmentPackage

            if (lb != other.lb) return false
            if (hb != other.hb) return false
            if (!flags.contentEquals(other.flags)) return false
            if (!timestamp.contentEquals(other.timestamp)) return false
            if (!sequence.contentEquals(other.sequence)) return false
            if (!length.contentEquals(other.length)) return false
            if (lengthInt != other.lengthInt) return false
            if (realDataLength != other.realDataLength) return false
            if (crc != other.crc) return false
            if (!reversed.contentEquals(other.reversed)) return false
            if (id != other.id) return false
            if (eof != other.eof) return false
            if (count != other.count) return false
            if (size != other.size) return false
            if (!data.contentEquals(other.data)) return false
            if (finish != other.finish) return false
            if (!overLeftData.contentEquals(other.overLeftData)) return false

            return true
        }

        override fun hashCode(): Int {
            var result = lb.toInt()
            result = 31 * result + hb
            result = 31 * result + flags.contentHashCode()
            result = 31 * result + timestamp.contentHashCode()
            result = 31 * result + sequence.contentHashCode()
            result = 31 * result + length.contentHashCode()
            result = 31 * result + lengthInt.toInt()
            result = 31 * result + realDataLength.toInt()
            result = 31 * result + crc
            result = 31 * result + reversed.contentHashCode()
            result = 31 * result + id.toInt()
            result = 31 * result + eof.toInt()
            result = 31 * result + count.toInt()
            result = 31 * result + size.toInt()
            result = 31 * result + data.contentHashCode()
            result = 31 * result + finish.hashCode()
            result = 31 * result + overLeftData.contentHashCode()
            return result
        }

        override fun toString(): String {
            return "PicFragmentPackage[parsePackageUnit,lb:${ObjectUtil.bytes2Hex(byteArrayOf(lb))},hb:${ObjectUtil.bytes2Hex(byteArrayOf(hb))},flags:${ObjectUtil.bytes2Hex(flags)},timestamp:${ObjectUtil.bytes2Hex(timestamp)}," +
                    "sequence:${ObjectUtil.bytes2Hex(sequence)},length:${ObjectUtil.bytes2Hex(length)},lengthInt:$lengthInt,realDataLength:$realDataLength,crc:${ObjectUtil.bytes2Hex(
                        byteArrayOf(crc))},reversed:${ObjectUtil.bytes2Hex(reversed)},id:$id,eof:$eof,count:$count,size:$size," + if (TestConfig.enableBytes2HexLog) "data:${ObjectUtil.bytes2Hex(data)}" else "dataSize:" + data.size.toString() + ",finish:$finish," + if (TestConfig.enableBytes2HexLog) "overLeftData:${ObjectUtil.bytes2Hex(overLeftData)}" else "overLeftDataSize" + overLeftData.size.toString() + "]"
        }
    }

    private fun parsePackageUnit(pData: ByteArray): PicFragmentPackage {
        /*val pHex = ObjectUtil.bytes2Hex(pData)*/
        if (pData.size < PACKAGE_HEAD_LENGTH /*|| !pHex.startsWith("d5f00100")*/) return PicFragmentPackage()
        val index = 0
        val lb = pData[index]
        val hb = pData[index + 1]
        val flags = ObjectUtil.subBytes(pData, index + 2, 2)
        val timestamp = pData.sliceArray(index + 4 .. index + 7)
        val sequence = pData.sliceArray(index + 8 .. index + 9)
        val length = pData.sliceArray(index + 10 .. index + 11)
        val lengthInt = ObjectUtil.bytes2Int(byteArrayOf(0x00, 0x00, length[1], length[0])).toUInt()
        val realDataLength = lengthInt - 4u
        val crc = pData[index + 12]
        val reversed = pData.sliceArray(index + 13 .. index + 15)
        val id = pData[index + 16].toUByte()
        val eof = pData[index + 17].toUByte()
        val count = pData[index + 18].toUByte()
        val size = pData[index + 19].toUByte()
        val data = pData.sliceArray(index + PACKAGE_HEAD_LENGTH until index + PACKAGE_HEAD_LENGTH + realDataLength.toInt())
        val finish = (pData.size - PACKAGE_HEAD_LENGTH).toUInt() >= realDataLength
        val overLeftData = if (finish) pData.sliceArray(index + PACKAGE_HEAD_LENGTH + data.size until pData.size) else byteArrayOf()
        return PicFragmentPackage(lb, hb, flags, timestamp, sequence, length, lengthInt, realDataLength, crc, reversed, id, eof, count, size, data, finish, overLeftData)
    }

    private fun parsePic2(data: ByteArray) {
        handleLog("parsePic2," + if (TestConfig.enableBytes2HexLog) "data:${ObjectUtil.bytes2Hex(data)}" else "dataSize:" + data.size.toString())
        var startIndex = -1
        if (data.size >= PACKAGE_START_DATA.size) {
            data.forEachIndexed { index, _ ->
                if (index <= data.size -4 && data[index] == PACKAGE_START_DATA[0] && data[index + 1] == PACKAGE_START_DATA[1] && data[index + 2] == PACKAGE_START_DATA[2] && data[index + 3] == PACKAGE_START_DATA[3]) {
                    startIndex = index
                    handleLog("test===== startIndex:$startIndex")
                    return@forEachIndexed
                }
            }
        }
        //val hexData = ObjectUtil.bytes2Hex(data)
            handleLog("parsePic2," + if (TestConfig.enableBytes2HexLog) "overLeftData:${ObjectUtil.bytes2Hex(overLeftData)}" else "overLeftDataSize:" + overLeftData.size.toString() + ",startIndex:$startIndex")
        if (overLeftData.isEmpty()) {
            handleLog("parsePic2,data:overLeftData.isEmpty() is true")
            if (singlePackageData.isEmpty() && /*hexData.contains(PACKAGE_START_DATA_HEX)*/startIndex >= 0) {
                handleLog("parsePic2,singlePackageData.isEmpty() && hexData.contains(PACKAGE_START_DATA) is true")
                singlePackageData = data.sliceArray(startIndex until data.size)
                parseSinglePackageData()
            } else {
                handleLog("parsePic2,singlePackageData.isEmpty() && hexData.contains(PACKAGE_START_DATA) is false")
                singlePackageData = singlePackageData.plus(data)
                parseSinglePackageData()
            }
        } else {
            handleLog("parsePic2,data:overLeftData.isEmpty() is false")
            singlePackageData = overLeftData.plus(data)
            parseSinglePackageData()
        }
    }

    private fun parseSinglePackageData() {
        handleLog("parseSinglePackageData," + if (TestConfig.enableBytes2HexLog) "singlePackageData:${ObjectUtil.bytes2Hex(singlePackageData)}" else "singlePackageDataSize:" + singlePackageData.size.toString())
        if (singlePackageData.size >= PACKAGE_HEAD_LENGTH) {
            var hasMultiPackage = hasMultiPackage(singlePackageData)
            if (hasMultiPackage) {
                while (hasMultiPackage) {
                    cPicFragmentPackage = parsePackageUnit(singlePackageData)
                    handleLog("cPicFragmentPackage:$cPicFragmentPackage")
                    if (cPicFragmentPackage.finish) {
                        val addResult = picFragmentPackageQueue.add(cPicFragmentPackage)
                        singlePackageData = cPicFragmentPackage.overLeftData
                        overLeftData = singlePackageData
                        handleLog("产生图片帧片段:$cPicFragmentPackage,addResult:$addResult")
                    } else {
                        overLeftData = cPicFragmentPackage.data
                    }
                    hasMultiPackage = hasMultiPackage(singlePackageData)
                }
            } else {
                overLeftData = singlePackageData
            }
        }
    }

    private fun hasMultiPackage(dataArea: ByteArray): Boolean {
        //val dataAreaHex = ObjectUtil.bytes2Hex(dataArea)
        if (dataArea.isEmpty() || dataArea.size < PACKAGE_HEAD_LENGTH || !/*dataAreaHex.contains(PACKAGE_START_DATA)*/dataArea.sliceArray(0 .. 3).contentEquals(
                byteArrayOf(0xd5.toByte(), 0xf0.toByte(), 0x01, 0x00)
            )) {
            return false
        }
        val index = /*dataAreaHex.indexOf(PACKAGE_START_DATA) / 2*/0
        val length = dataArea.sliceArray(index + 10 .. index + 11)
        val lengthInt = ObjectUtil.bytes2Int(byteArrayOf(0x00, 0x00, length[1], length[0]))
        return dataArea.size - index - PACKAGE_HEAD_LENGTH >= lengthInt - 4
    }

    fun stopPlayback() {
        running = false
        receiveQueueRunning = false
        receiveQueue.clear()
        //picRunning = false
        picFragmentPackageQueueRunning = false
        picFragmentPackageQueue.clear()
    }

    fun resumePlay() {
        receiveQueue.clear()
        picFragmentPackageQueue.clear()
        picFragmentPackageQueueRunning = true
        running = true
        receiveQueueRunning = true
    }

    fun startRecordVideo(context: Context): File {
        videoFilePath = context.filesDir.absolutePath + File.separator + "Download" + File.separator + "Video" + File.separator + "video_" + System.currentTimeMillis() + ".mp4"
        val file = File(videoFilePath)
        try {
            if (!file.parentFile.exists()) {
                file.parentFile.mkdirs()
                if (!file.exists()) {
                    file.createNewFile()
                }
            }
            videoDataRestoreOutputStream = FileOutputStream(file)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        TestConfig.enableVideoDataRestore = true
        return File(videoFilePath)
    }

    fun stopRecordVideo() {
        TestConfig.enableVideoDataRestore = false
        videoDataRestoreOutputStream!!.close()
        videoDataRestoreOutputStream = null
    }

    fun destory() {
        stopPlayback()
        socket.close()
        try {
            if (sourceDataRestoreOutputStream != null) {
                sourceDataRestoreOutputStream!!.close()
            }
            if (logFileOutputStream != null) {
                logFileOutputStream!!.close()
            }
            if (videoDataRestoreOutputStream != null) {
                videoDataRestoreOutputStream!!.close()
            }
            //socket.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    companion object {
        private const val PACKAGE_START_DATA_HEX = "d5f00100"
        private val PACKAGE_START_DATA = byteArrayOf(0xd5.toByte(), 0xf0.toByte(), 0x01.toByte(), 0x00.toByte())
        private const val PACKAGE_HEAD_LENGTH = 20

        private var logFileOutputStream: FileOutputStream? = null

        private fun caculateCrc(data: ByteArray): Byte {
            var crc = 0xff.toByte()
            data.forEachIndexed { index, byte ->
                crc = crcData[(crc.toInt() xor byte.toInt()) and 0xff]
            }
            return crc
        }

        private val crcData = byteArrayOf(0x00.toByte(), 0xF7.toByte(), 0xB9.toByte(), 0x4E.toByte(), 0x25.toByte(), 0xD2.toByte(), 0x9C.toByte(), 0x6B.toByte(),
            0x4A.toByte(), 0xBD.toByte(), 0xF3.toByte(), 0x04.toByte(), 0x6F.toByte(), 0x98.toByte(), 0xD6.toByte(), 0x21.toByte(),
            0x94.toByte(), 0x63.toByte(), 0x2D.toByte(), 0xDA.toByte(), 0xB1.toByte(), 0x46.toByte(), 0x08.toByte(), 0xFF.toByte(),
            0xDE.toByte(), 0x29.toByte(), 0x67.toByte(), 0x90.toByte(), 0xFB.toByte(), 0x0C.toByte(), 0x42.toByte(), 0xB5.toByte(),
            0x7F.toByte(), 0x88.toByte(), 0xC6.toByte(), 0x31.toByte(), 0x5A.toByte(), 0xAD.toByte(), 0xE3.toByte(), 0x14.toByte(),
            0x35.toByte(), 0xC2.toByte(), 0x8C.toByte(), 0x7B.toByte(), 0x10.toByte(), 0xE7.toByte(), 0xA9.toByte(), 0x5E.toByte(),
            0xEB.toByte(), 0x1C.toByte(), 0x52.toByte(), 0xA5.toByte(), 0xCE.toByte(), 0x39.toByte(), 0x77.toByte(), 0x80.toByte(),
            0xA1.toByte(), 0x56.toByte(), 0x18.toByte(), 0xEF.toByte(), 0x84.toByte(), 0x73.toByte(), 0x3D.toByte(), 0xCA.toByte(),
            0xFE.toByte(), 0x09.toByte(), 0x47.toByte(), 0xB0.toByte(), 0xDB.toByte(), 0x2C.toByte(), 0x62.toByte(), 0x95.toByte(),
            0xB4.toByte(), 0x43.toByte(), 0x0D.toByte(), 0xFA.toByte(), 0x91.toByte(), 0x66.toByte(), 0x28.toByte(), 0xDF.toByte(),
            0x6A.toByte(), 0x9D.toByte(), 0xD3.toByte(), 0x24.toByte(), 0x4F.toByte(), 0xB8.toByte(), 0xF6.toByte(), 0x01.toByte(),
            0x20.toByte(), 0xD7.toByte(), 0x99.toByte(), 0x6E.toByte(), 0x05.toByte(), 0xF2.toByte(), 0xBC.toByte(), 0x4B.toByte(),
            0x81.toByte(), 0x76.toByte(), 0x38.toByte(), 0xCF.toByte(), 0xA4.toByte(), 0x53.toByte(), 0x1D.toByte(), 0xEA.toByte(),
            0xCB.toByte(), 0x3C.toByte(), 0x72.toByte(), 0x85.toByte(), 0xEE.toByte(), 0x19.toByte(), 0x57.toByte(), 0xA0.toByte(),
            0x15.toByte(), 0xE2.toByte(), 0xAC.toByte(), 0x5B.toByte(), 0x30.toByte(), 0xC7.toByte(), 0x89.toByte(), 0x7E.toByte(),
            0x5F.toByte(), 0xA8.toByte(), 0xE6.toByte(), 0x11.toByte(), 0x7A.toByte(), 0x8D.toByte(), 0xC3.toByte(), 0x34.toByte(),
            0xAB.toByte(), 0x5C.toByte(), 0x12.toByte(), 0xE5.toByte(), 0x8E.toByte(), 0x79.toByte(), 0x37.toByte(), 0xC0.toByte(),
            0xE1.toByte(), 0x16.toByte(), 0x58.toByte(), 0xAF.toByte(), 0xC4.toByte(), 0x33.toByte(), 0x7D.toByte(), 0x8A.toByte(),
            0x3F.toByte(), 0xC8.toByte(), 0x86.toByte(), 0x71.toByte(), 0x1A.toByte(), 0xED.toByte(), 0xA3.toByte(), 0x54.toByte(),
            0x75.toByte(), 0x82.toByte(), 0xCC.toByte(), 0x3B.toByte(), 0x50.toByte(), 0xA7.toByte(), 0xE9.toByte(), 0x1E.toByte(),
            0xD4.toByte(), 0x23.toByte(), 0x6D.toByte(), 0x9A.toByte(), 0xF1.toByte(), 0x06.toByte(), 0x48.toByte(), 0xBF.toByte(),
            0x9E.toByte(), 0x69.toByte(), 0x27.toByte(), 0xD0.toByte(), 0xBB.toByte(), 0x4C.toByte(), 0x02.toByte(), 0xF5.toByte(),
            0x40.toByte(), 0xB7.toByte(), 0xF9.toByte(), 0x0E.toByte(), 0x65.toByte(), 0x92.toByte(), 0xDC.toByte(), 0x2B.toByte(),
            0x0A.toByte(), 0xFD.toByte(), 0xB3.toByte(), 0x44.toByte(), 0x2F.toByte(), 0xD8.toByte(), 0x96.toByte(), 0x61.toByte(),
            0x55.toByte(), 0xA2.toByte(), 0xEC.toByte(), 0x1B.toByte(), 0x70.toByte(), 0x87.toByte(), 0xC9.toByte(), 0x3E.toByte(),
            0x1F.toByte(), 0xE8.toByte(), 0xA6.toByte(), 0x51.toByte(), 0x3A.toByte(), 0xCD.toByte(), 0x83.toByte(), 0x74.toByte(),
            0xC1.toByte(), 0x36.toByte(), 0x78.toByte(), 0x8F.toByte(), 0xE4.toByte(), 0x13.toByte(), 0x5D.toByte(), 0xAA.toByte(),
            0x8B.toByte(), 0x7C.toByte(), 0x32.toByte(), 0xC5.toByte(), 0xAE.toByte(), 0x59.toByte(), 0x17.toByte(), 0xE0.toByte(),
            0x2A.toByte(), 0xDD.toByte(), 0x93.toByte(), 0x64.toByte(), 0x0F.toByte(), 0xF8.toByte(), 0xB6.toByte(), 0x41.toByte(),
            0x60.toByte(), 0x97.toByte(), 0xD9.toByte(), 0x2E.toByte(), 0x45.toByte(), 0xB2.toByte(), 0xFC.toByte(), 0x0B.toByte(),
            0xBE.toByte(), 0x49.toByte(), 0x07.toByte(), 0xF0.toByte(), 0x9B.toByte(), 0x6C.toByte(), 0x22.toByte(), 0xD5.toByte(),
            0xF4.toByte(), 0x03.toByte(), 0x4D.toByte(), 0xBA.toByte(), 0xD1.toByte(), 0x26.toByte(), 0x68.toByte(), 0x9F.toByte(),
        )

        fun handleLog(msg: String) {
            if (TestConfig.enableConsoleLog) {
                var str = msg
                str = str.replace(" ", "")
                var index = 0
                val maxLength = 4000
                var finalString: String
                while (index < str.length) {
                    finalString =
                        if (str.length <= index + maxLength) str.substring(index) else str.substring(
                            index,
                            index + maxLength
                        )
                    index += maxLength
                    Log.d("MjpegMediaCodecPlayer", finalString.replace(" ", ""))
                }
            }
            val logAction = {
                try {
                    val timeStr = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(Date())
                    logFileOutputStream!!.write("$timeStr  $msg\n".toByteArray())
                } catch (e: IOException) {
                    throw RuntimeException(e)
                }
            }
            if (TestConfig.enableFileLog) {
                if (TestConfig.fileLogNewThread) {
                    Thread {
                        logAction()
                    }.start()
                } else {
                    logAction()
                }
            }
        }
    }
}